/**
 * Copyright (c) 2008 Trent Hoeppner
 * 
 * This file is part of BillCalc.
 * 
 * BillCalc is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * BillCalc is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with BillCalc.  If not, see <http://www.gnu.org/licenses/>.
 **/

package com.trentech.billcalc;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import testcases.MainTest;

/**
 * Lease represents the terms related to finance for renting the apartment, or 
 * even a room in the apartment.
 * 
 * Events
 *   BillDamage
 * 
 * @author Trent Hoeppner
 */
public class Lease {
    
    /**
     * The landlord or subletter.
     */
    private Entity controller;
    
    
    /**
     * The tenant.
     */
    private Entity payer;
    
    
    /**
     * The dates that the lease is valid for.
     */
    private DateGroup leasePeriod;
    
    
    /**
     * The list of dates when the rent is due.  This should be sorted from 
     * oldest to newest.
     */
    private List<Date> periodStarts;
    
    
    /**
     * The amount of money for rent each month.
     */
    private int amountPerMonth;
    
    
    /**
     * The amount of money for the deposit.
     */
    private int deposit;
    
    
    /**
     * True indicates that subletting is allowed, false indicates that it is 
     * not allowed.
     */
    private boolean canSublet;
    
    
    /**
     * The parent lease if controller is a subletter, or null if controller is 
     * the landlord.
     */
    private Lease parentLease;
    
    
    /**
     * A description of what is being rented - e.g. the whole apartment or 
     * which room in the apartment.
     */
    private String roomDescription;
    
    
    /**
     * The amount of money to charge if the lease is terminated early.
     */
    private int leaseBreakPenalty;
    
    
    /**
     * The number of months between rent payments.
     */
    private int paymentFrequency;
    

    /**
     * Constructor for Lease.
     * 
     * @param   controller          the landlord or subletter.  Cannot be null.
     * @param   payer               the tenant.  Cannot be null.
     * @param   leasePeriod         the times when the lease is valid 
     *                              (excluding the last day).  Cannot be null.
     * @param   amountPerMonth      the amount of money to pay for one month.  
     *                              Must be >= 0.
     * @param   deposit             the amount of money for the deposit.  Must 
     *                              be >= 0.
     * @param   canSublet           true indicates that the tenant can sublet, 
     *                              false indicates that subletting is not 
     *                              allowed.
     * @param   roomDescription     a description of what is being rented (e.g. 
     *                              one room or the apartment).  Cannot be null.
     * @param   parentLease         if controller is a subletter, parentLease 
     *                              is the lease that controller has with his 
     *                              landlord (or another subletter).  Otherwise 
     *                              this should be null.
     * @param   leaseBreakPenalty   the amount of money to charge for breaking 
     *                              the lease.  Must be >= 0.
     * @param   paymentFrequency    the number of months between rent payments.
     *                              Must be > 0.
     */
    public Lease(Entity controller, Entity payer, DateGroup leasePeriod, 
            int amountPerMonth, int deposit, boolean canSublet, 
            Lease parentLease, String roomDescription, int leaseBreakPenalty, 
            int paymentFrequency) {
        this.controller = controller;
        this.payer = payer;
        this.leasePeriod = leasePeriod;
        
        this.amountPerMonth = amountPerMonth;
        this.deposit = deposit;
        this.canSublet = canSublet;
        this.roomDescription = roomDescription;
        this.parentLease = parentLease;
        this.leaseBreakPenalty = leaseBreakPenalty;
        this.paymentFrequency = paymentFrequency;

        this.periodStarts = createPeriodStarts(leasePeriod.getFirstDate(), 
                leasePeriod.getLastDate());
    }


    /**
     * Returns the landlord or subletter.
     * 
     * @return  the landlord or subletter.  Will not be null.
     */
    public Entity getController() {
        return controller;
    }


    /**
     * Returns the tenant.
     * 
     * @return  the tenant.  Will not be null.
     */
    public Entity getPayer() {
        return payer;
    }
    
    
    /**
     * Sets the tenant.
     * 
     * @param   payer   the tenant.  Cannot be null.
     */
    public void setPayer(Entity payer) {
        this.payer = payer;
    }


    /**
     * Returns the dates that the lease is valid for.
     * 
     * @return  the dates that the lease is valid for.  Will not be null.
     */
    public DateGroup getLeasePeriod() {
        return leasePeriod;
    }
    
    
    /**
     * Sets the leasePeriod for this.
     * 
     * @param   leasePeriod the lease period to set.  Must have a beginning and 
     *                      an end.  Cannot be null.
     */
    public void setLeasePeriod(DateGroup leasePeriod) {
        this.leasePeriod = leasePeriod;
    }


    /**
     * Returns the dates when the rent is due.
     * 
     * @return  the dates when the rent is due.  Will not be null or empty.
     */
    public List<Date> getPeriodStarts() {
        return periodStarts;
    }


    /**
     * Returns the amount of rent per month.
     * 
     * @return  the amount of rent per month.  Will be >= 0.
     */
    public int getAmountPerMonth() {
        return amountPerMonth;
    }


    /**
     * Returns the amount of money for the deposit.
     * 
     * @return  the amount of money for the deposit.  Will be >= 0.
     */
    public int getDeposit() {
        return deposit;
    }


    /**
     * Returns whether subletting is allowed.
     * 
     * @return  true if the tenant can sublet, false otherwise.
     */
    public boolean isCanSublet() {
        return canSublet;
    }


    /**
     * Returns the parent lease.
     * 
     * @return  the parent lease, or null if there is no parent (the landlord 
     *          is the owner in this case).
     */
    public Lease getParentLease() {
        return parentLease;
    }


    /**
     * Returns a description of the room.
     *  
     * @return  a description of the room.  Will not be null.
     */
    public String getRoomDescription() {
        return roomDescription;
    }


    /**
     * Returns the amount of money charged if the lease is broken.
     * 
     * @return  the amount of money to charge if the lease is broken.  Will be 
     *          >= 0.
     */
    public int getLeaseBreakPenalty() {
        return leaseBreakPenalty;
    }


    /**
     * Sets the amount of money to charge if the lease is broken.
     * 
     * @param   leaseBreakPenalty   the amount of money to charge if the lease 
     *                              is broken.  Must be >= 0.
     */
    public void setLeaseBreakPenalty(int leaseBreakPenalty) {
        this.leaseBreakPenalty = leaseBreakPenalty;
    }
    
    
    /**
     * Returns the number of payment periods in the lease.
     * 
     * @return  the number of payment periods.  Will be >= 1.
     */
    public int getNumPeriods() {
        return periodStarts.size();
    }
    
    
    /**
     * Returns the payment period for the given 0-based index.
     * 
     * @param   index   the index for the desired payment period.  Must be 
     *                  >= 0.
     *                  
     * @return  the payment period for the given index, starting on the first 
     *          day (inclusive) and ending on the last day (exclusive).  Will 
     *          not be null.
     */
    public DateGroup getPeriod(int index) {
        Date periodStart = periodStarts.get(index);
        Date periodEnd;
        
        // original : 
        // if (index + 1 < periodStarts.size()) {
    	// seed fault : 
        if (index + (MainTest.isOriginal || MainTest.isFaultFound ? 1 : 0) < periodStarts.size()) {
        	
            periodEnd = periodStarts.get(index + 1);
        }
        else {
            periodEnd = leasePeriod.getLastDate();
        }
        
        DateGroup dateGroup = new DateGroup();
        dateGroup.startRange(periodStart);
        dateGroup.endRange(periodEnd);
        return dateGroup;
    }
    
    
    /**
     * Adjusts the end of the lease to a higher value, for lease renewals.
     * 
     * @param   leaseEnd    the new end of the lease.  Cannot be null, and must
     *                      be after the current end.
     */
    public void adjustLeaseEnd(Date leaseEnd) {
        Date startDate = leasePeriod.getFirstDate();
        Date oldEndDate = leasePeriod.getLastDate();
        DateGroup newLeasePeriod = new DateGroup();
        newLeasePeriod.startRange(startDate);
        newLeasePeriod.endRange(leaseEnd);
        leasePeriod = newLeasePeriod;
        
        List<Date> additionalPeriodStarts = createPeriodStarts(oldEndDate, leaseEnd);
        periodStarts.addAll(additionalPeriodStarts);
    }
    

    /**
     * Creates a list of dates that represent the start of each period in the 
     * lease, from start until end.
     * 
     * @param   start   the Date to start the first period from (inclusive).  
     *                  Cannot be null.
     * @param   end     the Date to end the last period (exclusive).  Cannot 
     *                  be null.
     * 
     * @return  the list of period starts.  Will not be null.
     */
    private List<Date> createPeriodStarts(Date start, Date end) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(start);
        List<Date> newPeriodStarts = new ArrayList<Date>();
        Date currentPeriodStart = start;
        while (currentPeriodStart.before(end)) {
            newPeriodStarts.add(currentPeriodStart);
            calendar.add(Calendar.MONTH, paymentFrequency);
            currentPeriodStart = calendar.getTime();
        }
        return newPeriodStarts;
    }


}
